IAMユーザーに特権を一時的に付与する簡易承認ワークフローをSystems Manager (Automation) で実装してみた
はじめに
中山(順)です
先日、Systems ManagerのAutomationでAWSのAPIをたたけるようになりました。
今回、これを使ってIAMユーザーに特権を一時的に付与する簡易承認ワークフローを作ってみました。
この記事では、以下の流れで動かすまでの流れを紹介していきます。
- 要件を決める
- 実装方針を決める
- 実装する
- 動かす
想定要件
以下のような要件を設定しました。
- 指定したIAM Userに対して、一時的に特権を付与したい
- 特権を付与したい時間帯を指定したい
- 特権の付与には上長の承認が必要
実装方針
これらの要件を実現するために何の機能を利用するかを考えてみました。
先般のアップデートと併せて、必要な材料が揃っていることがわかるかと思います。
"指定したIAMに対して、特権を付与したい"
今回は特権をIAM Roleとして事前に定義する前提にしたいと思います。 そのため、権限の付与はIAM Userに対して特権を持つIAM RoleにAssumeRoleする権限を付与することで実現できます。
スイッチ先のIAM Roleの信頼関係 (Trust Policy) で制御することも選択肢としてはあり得そうですが、めんどくさそうだったのでやめときました。
"特権を付与したい時間帯を指定したい"
IAMポリシーではDate Condition Operatorsを利用することができます。 これにより、「現在時刻が、XXXX/XX/XX XX:XX:XX 以降 なおかつ YYYY/YY/YY YY:YY:YY 以前であれば指定したアクションを許可する」などのポリシーを定義できます。
"特権の付与には上長の承認が必要"
Automationでは承認のステップを定義できます。
処理の流れ
今回はシンプルなワークフローと言うことで、以下の2ステップの処理を実行するようにします。
- 承認者に承認を求め、承認してもらう
- IAM Userに権限を付与する
その他
今回は以下のような感じで実装しようと思います。
ポリシーの設定
Managed Policyを作らず、権限を付与するIAM UserのInline Policyを作成することとします。
IAM Role の信頼関係
事前に作成する IAM Role にはスイッチ元になる可能性がある IAM User や AWS アカウントを信頼関係に追加してあることを前提とします。
やってみた
前提となるリソースの作成
動作確認に備え、前提となるリソースを追加します。
IAM User(申請者)
まずは、権限を付与されるIAM Userを作成します。
aws iam create-user \ --user-name cm-applicant
IAM User(承認者)
承認するIAM Userを作成します。
aws iam create-user \ --user-name cm-approver
承認するIAM Userには、承認する権限が必要です。逆に職務権限的に承認できてはいけないメンバーのIAM Userには承認する権限を付与してはいけませんのでご注意ください。
また、マネージメントコンソールで承認する場合にはマネージメントコンソールにログインできるようパスワードを別途設定してください。 CLIでも承認可能ですが、その場合はアクセスキーが必要です。
aws iam attach-user-policy \ --user-name cm-approver \ --policy-arn arn:aws:iam::aws:policy/AmazonSSMAutomationApproverAccess
SNS Topic(承認者への通知)
承認を求められるメンバーに対してメールで通知を行うために SNS Topic を作成します。
aws sns create-topic \ --name automation-approver
Automationによる承認は複数名の承認を必要とするように設定できます。 承認権限を持つメンバーのアドレスを複数登録するか、メーリングリストなどを設定しましょう。 subscribeコマンドを実行した後にはAWSから送信されてくるメールのリンクをクリックしてsubscriptionを承認しましょう。
aws sns subscribe \ --topic-arn arn:aws:sns:ap-northeast-1:XXXXXXXXXXXX:automation-approver \ --protocol email \ --notification-endpoint approver@example.com
IAM Role(特権)
特権を持つロールを作成します。
今回は、同一アカウント内のIAM Userから利用されることを想定して、自身のAWSアカウントのみを信頼関係に設定します。
TRUST_POLICY_FILE_NAME='TrustPolicy.json' cat << EOF > ${TRUST_POLICY_FILE_NAME} { "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::XXXXXXXXXXXX:root" }, "Action": "sts:AssumeRole" } ] } EOF cat ${TRUST_POLICY_FILE_NAME} jsonlint -q ${TRUST_POLICY_FILE_NAME}
今回は、AdministratorAccessをIAM Roleにアタッチします。また、取得できる一時認証情報が有効な時間は最小の1時間とします。
aws iam create-role \ --role-name PrivilegeRole \ --assume-role-policy-document file://${TRUST_POLICY_FILE_NAME} \ --max-session-duration 3600 aws iam attach-role-policy \ --role-name PrivilegeRole \ --policy-arn arn:aws:iam::aws:policy/AdministratorAccess
ドキュメント
作成したドキュメントはこちらです。
--- schemaVersion: "0.3" description: "Temporary Privilege Workflow" parameters: IamUserName: type: "String" description: "(Required) IAM User name for applicant" default: "cm-applicant" IamRoleArn: type: "String" description: "(Required) ARN of Privilege IAM Role" SnsTopicArn: type: "String" description: "(Required) SNS Topic ARN for Approvers" Approvers: type: "StringList" description: "(Required) IAM user or user arn of approvers" StartTime: type: "String" description: "(Required) Start Time that applicant can invoke AssumeRole" default: "2018-08-30T09:00:00Z" EndTime: type: "String" description: "(Required) End Time that applicant can invoke AssumeRole" default: "2018-08-30T18:00:00Z" mainSteps: - action: "aws:approve" name: "approve" inputs: NotificationArn: "{{SnsTopicArn}}" Message: "Do you approve?" Approvers: "{{Approvers}}" MinRequiredApprovals: 1 onFailure: "Abort" - action: "aws:executeAwsApi" name: "PutInlinepolicy" inputs: Service: "iam" Api: "PutUserPolicy" UserName: "{{IamUserName}}" PolicyName: "AllowAssumePrivilegeRole" PolicyDocument: "{\"Version\":\"2012-10-17\",\"Statement\":[{\"Effect\":\"Allow\",\"Action\":\"sts:AssumeRole\",\"Resource\":\"{{IamRoleArn}}\",\"Condition\":{\"DateGreaterThan\":{\"aws:CurrentTime\":\"{{StartTime}}\"},\"DateLessThan\":{\"aws:CurrentTime\":\"{{EndTime}}\"}}}]}" isEnd: true
補足/留意点
このAutomationを利用してIAM Userに対して継続的に権限を付与する場合、前回のポリシーが上書きされます(同じ名前のインラインポリシーを PUT します)。 Conditionに時刻を利用しているので前回指定した時間帯を過ぎていれば何も影響はありません。
また、指定した時間帯は「Assume Roleができる」時間帯です。そのため、特権を利用できる時間帯は最大で指定した時間帯+一時認証情報の有効期間(今回は1時間)となります。 今回はそういう仕様です。
動作確認
それでは、作成したAutomationのドキュメントを実行してみましょう。
まずは必要なパラメーターを入力します。
すると、承認者のメールアドレス宛にメールが送られてきます。 承認はマネージメントコンソールだけでなく、CLIでも可能です。
マネージメントコンソールで承認する場合には、指定した承認者のIAM Userでログインしておく必要があります。 承認画面は以下のような感じです。
承認して次のステップに進み成功した結果がこちらです。
それでは、IAM Userにポリシーが設定されているかを確認します。
aws iam list-user-policies \ --user-name cm-applicant
{ "PolicyNames": [ "AllowAssumePrivilegeRole" ] }
ポリシードキュメントも確認しましょう。
aws iam get-user-policy \ --user-name cm-applicant \ --policy-name AllowAssumePrivilegeRole
{ "UserName": "cm-applicant", "PolicyName": "AllowAssumePrivilegeRole", "PolicyDocument": { "Version": "2012-10-17", "Statement": [ { "Action": "sts:AssumeRole", "Resource": "arn:aws:iam::XXXXXXXXXXXX:role/PrivilegeRole", "Effect": "Allow", "Condition": { "DateLessThan": { "aws:CurrentTime": "2018-08-31T00:00:00Z" }, "DateGreaterThan": { "aws:CurrentTime": "2018-08-30T16:00:00Z" } } } ] } }
このIAM UserでAssume Roleできるか確認してみます。(今回は事前に申請者のアクセスキーを設定しています。)
aws sts assume-role \ --role-arn arn:aws:iam::XXXXXXXXXXXX:role/PrivilegeRole \ --role-session-name cm-applicant-test-privilege
{ "AssumedRoleUser": { "AssumedRoleId": "AROAJDJMMH7FUUV4JVCHM:cm-applicant-test-privilege", "Arn": "arn:aws:sts::XXXXXXXXXXXX:assumed-role/PrivilegeRole/cm-applicant-test-privilege" }, "Credentials": { "SecretAccessKey": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "SessionToken": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx", "Expiration": "2018-08-30T17:17:17Z", "AccessKeyId": "xxxxxxxxxxxxxxxxxxxxxxxx" } }
できました!
まとめ
こんな感じで、ちょっとした「一連の処理」をAutomationで定義できます。
任意のAPIを呼び出せるので、今回のような処理以外にもいろんな用途に利用することができるでしょう。 可能性は無限大です。
また、入力フォームに必要事項を入力して実行するだけですので、オペレーターに要求されるスキルはぐっと下がりますし、作業の精度も効率も上がるのではないでしょうか。
ドキュメントの開発にあたっては、1つのドキュメントを作り込み過ぎずカジュアルに作ってカジュアルに捨ててを繰り返すといい感じになりそうな気がします、知らんけど。
現場からは以上です。